當我們在寫 React 時, 一個 component 是怎麼「出生 → 更新 → 死亡」的?
這段過程就叫做 生命週期 (Lifecycle)。
理解生命週期,不只可以幫助我們正確放置程式邏輯,也能避免常見的錯誤,例如 API 被打爆、memory leak。
React component 的生命週期分三段:
Class 用 lifecycle methods;Function 用 Hooks 來表達同一件事。
class Example extends React.Component {
// 正確:這裡才是初始化 state 的時機
state = { message: "Hello" };
render() { return <h1>{this.state.message}</h1>; }
}
componentDidMount
setState
,會觸發第二次 render,有機會造成畫面跳動。class Example extends React.Component {
state = { message: "Hello" };
componentDidMount() {
// 這是「掛載後更新」,不是初始化
this.setState({ message: "Mounted!" });
}
}
componentDidUpdate(prevProps, prevState)
componentDidUpdate
setState
,否則會陷入無限迴圈componentDidUpdate(prevProps) {
if (this.props.userId !== prevProps.userId) {
this.fetchUser(this.props.userId);
}
}
componentWillUnmount
clearInterval
、移除事件監聽、關閉 WebSocket…)以避免 memory leakFunction component 同樣經歷 mount/update/unmount,但用 Hooks 寫:
useState
的初始值(或 lazy initializer)function Example() {
const [message] = React.useState(() => "Hello"); // 這裡才是初始化
return <h1>{message}</h1>;
}
useEffect
// 掛載後執行一次(相當於 didMount)
useEffect(() => {
fetch("/api/data").then(/* ... */);
}, []);
// 依賴改變時執行(相當於 didUpdate, 注意:首次掛載也會跑一次)
useEffect(() => {
console.log("count changed:", count);
}, [count]);
// 卸載時清理(相當於 willUnmount)
useEffect(() => {
const id = setInterval(/* ... */);
return () => clearInterval(id);
}, []);
fetch()
、操作 DOM、setTimeout
useEffect
/ lifecycle,render 只計算 UIsetInterval
、事件監聽、WebSocket 沒清 → memory leak
componentWillUnmount
或 useEffect
的 return 函式 清理setTimeout
多半會自動結束,但若會在 unmount 後觸發 setState
,也應清理或取消componentDidUpdate
無條件 setState
prevProps/prevState
再決定是否更新useState
初始值(Function)componentDidMount
/ useEffect
(抓資料、訂閱、DOM 操作)componentDidUpdate
+ 比較 prev 值,或 useEffect([deps])
componentWillUnmount
或 useEffect
的 return原則:render 保持純粹(UI = f(state, props));和外界互動的一切(I/O、訂閱、計時器、DOM),都交給 effect 階段。
The lifecycle of a React component describes the stages from mount to update to unmount.
In class components, we have methods like componentDidMount, componentDidUpdate, and componentWillUnmount.
In function components, we use useEffect to handle these phases: an empty dependency array works like componentDidMount, a dependency array works like componentDidUpdate, and the return function acts like componentWillUnmount.
A common mistake is placing side effects inside render, which causes unnecessary work. Instead, we always put side effects in useEffect and clean them up properly to avoid memory leaks.
React 元件的生命週期,就是從「被掛載(mount)」→「更新(update)」→「被移除(unmount)」的過程。
在 Class Component 裡,有一些方法可以處理這些階段,例如:componentDidMount
、componentDidUpdate
、componentWillUnmount
。
在 Function Component 裡,則是用useEffect
來完成:
- 空的依賴陣列(
[]
)就像componentDidMount
(只在掛載後跑一次)。- 帶有依賴的陣列(
[deps]
)就像componentDidUpdate
(依賴改變時跑)。useEffect
裡的 return function,則像componentWillUnmount
(卸載前清理)。
常見的錯誤是把 副作用直接寫在 render 裡,這會造成多餘的操作。正確做法是把副作用放進useEffect
,並記得清理,這樣才能避免記憶體洩漏。
componentDidMount
;初始化應該在 render 前 完成。componentDidMount
/ useEffect([])
是用來「掛載後做副作用」。componentDidUpdate
/ useEffect([deps])
用來「變更後做事」,要加條件避免迴圈。